package pCloud.service;

import java.net.InetSocketAddress;

import pCloud.message.ClientResponse;
import pCloud.message.Message;
import pCloud.message.NumberMessage;
import pCloud.message.ReplyMessage;
import pCloud.message.RequestFile;
import pCloud.transport.SocketCommunicator;

/**
 * Handle messages between proxy server and the data server.
 */
public class ProxyServerDataServerProtHandle extends ProtocolHandle {

	CachingHandle cacheHandler = null;
	IncompleteResponse incompleteResponses;

	@Override
	public void receive(Object o) throws Exception {
		this.handleDataServerMsg((Message) o);
	}

	@Override
	public void send(Object m) throws Exception {
		this.conn.send(m);

	}

	public void initializeCacheHandler(CachingHandle c) {
		this.cacheHandler = c;
	}

	public void initializeIncompleteResponse(IncompleteResponse ir) {
		this.incompleteResponses = ir;
	}

	private void handleDataServerMsg(Message msg) throws Exception {
		if (msg.getMessageType() != Message.DATA_TO_PROXY_MSG) {
			System.err.println("DATA_TO_PROXY msg expected, got "
					+ msg.getMessageType());
			return;
		}
		this.communicator.getLogger().debug(
				"DATA_TO_PROXY msg received from " + msg.getFrom());

		// 1. Unwrapp the message
		Message unwrappedMsg = (Message) msg.getMessageContent();

		if (unwrappedMsg.getMessageType() != Message.PROXY_TO_CLIEN_MSG) {
			System.err
					.println("Wrong type of message, expecting PROXY_TO_CLIENT, receiving "
							+ unwrappedMsg.getMessageType());
			return;
		}

		// Forward the response to be joined, in case of data response
		if (unwrappedMsg.getMessageContent() instanceof ReplyMessage) {
			this.handleDataReply(
					(ReplyMessage) unwrappedMsg.getMessageContent(),
					msg.getFrom());
		}

		else {
			unwrappedMsg.setMessageType(Message.PROXY_TO_CLIEN_MSG);
			// 2. Forward the message to the client. Catch the exception
			// so that the connection remains even when the sending back to the
			// client failed
			try {
				this.communicator.send(unwrappedMsg.getTo(), unwrappedMsg);
				this.communicator.getConnection(unwrappedMsg.getTo()).close();
				
			} catch (Exception e) {
				this.communicator.getLogger().debug(
						"Forwarding back to " + unwrappedMsg.getTo()
								+ " failed, do nothing.");
			}
		}
	}

	private void handleDataReply(ReplyMessage msg, InetSocketAddress from) {
		ClientResponse response = this.incompleteResponses
				.getIncompleteResponse(msg.getRequestId());
		synchronized (response) {
			response.addReply(from, msg);
			response.notifyAll();
		}

		// adding to the appropriate cache
		if (this.cacheHandler != null) {
			RequestFile rf = (RequestFile) ((DataCaching) this.cacheHandler)
					.getRequestFile(msg.getRequestId(), from);
			
			this.cacheHandler.addToCache(rf, msg);
			((DataCaching) this.cacheHandler).removeFromTempMap(
					msg.getRequestId(), from);
		}
	}
}
